--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 0492e7d7bf956b0eacdb4fc40c6e87151db604e7
Parents : 6a23727
Author : Sudo-Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-01-04T23:19:17-06:00
refactor(components): update loading animations to use 'animate-spin-reverse' class for consistency
Changes
11 files changed, 85 insertions(+), 40 deletions(-)
Diff
diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue
index c0a8642b..b7ca8b88 100644
--- a/meshchatx/src/frontend/components/App.vue
+++ b/meshchatx/src/frontend/components/App.vue
@@ -85,7 +85,7 @@
<MaterialDesignIcon
icon-name="refresh"
class="size-6"
- :class="{ 'animate-spin': isSyncingPropagationNode }"
+ :class="{ 'animate-spin-reverse': isSyncingPropagationNode }"
/>
<span class="hidden sm:inline-block my-auto mx-1 text-sm font-medium">{{
isSyncingPropagationNode
diff --git a/meshchatx/src/frontend/components/archives/ArchivesPage.vue b/meshchatx/src/frontend/components/archives/ArchivesPage.vue
index 9c4d9151..26affcec 100644
--- a/meshchatx/src/frontend/components/archives/ArchivesPage.vue
+++ b/meshchatx/src/frontend/components/archives/ArchivesPage.vue
@@ -178,7 +178,7 @@
<!-- Rendered Content -->
<div class="flex-1 overflow-y-auto p-4 nodeContainer overscroll-contain">
<div v-if="isLoading" class="flex items-center justify-center h-full text-zinc-500">
- <MaterialDesignIcon icon-name="refresh" class="size-8 animate-spin" />
+ <MaterialDesignIcon icon-name="refresh" class="size-8 animate-spin-reverse" />
</div>
<div
v-else-if="!viewingArchive"
diff --git a/meshchatx/src/frontend/components/blocked/BlockedPage.vue b/meshchatx/src/frontend/components/blocked/BlockedPage.vue
index 57a3b6d8..08df701f 100644
--- a/meshchatx/src/frontend/components/blocked/BlockedPage.vue
+++ b/meshchatx/src/frontend/components/blocked/BlockedPage.vue
@@ -31,7 +31,11 @@
title="Refresh"
@click="loadBlockedDestinations"
>
- <MaterialDesignIcon icon-name="refresh" class="size-6" :class="{ 'animate-spin': isLoading }" />
+ <MaterialDesignIcon
+ icon-name="refresh"
+ class="size-6"
+ :class="{ 'animate-spin-reverse': isLoading }"
+ />
</button>
</div>
</div>
diff --git a/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue b/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue
index 2b32841a..2b4b34fc 100644
--- a/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue
+++ b/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue
@@ -122,6 +122,7 @@
</optgroup>
<optgroup label="RNodes">
<option value="RNodeInterface">RNode Interface</option>
+ <option value="RNodeIPInterface">RNode IP Interface</option>
<option value="RNodeMultiInterface">RNode Multi Interface</option>
</optgroup>
<optgroup label="IP Networks">
@@ -263,20 +264,49 @@
<!-- RNode interface -->
<!-- interface port -->
- <div v-if="newInterfaceType === 'RNodeInterface'" class="mb-2">
- <FormLabel class="mb-1">Port</FormLabel>
- <select
- v-model="newInterfacePort"
- class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
- >
- <option v-for="comport of comports" :key="comport.device" :value="comport.device">
- {{ comport.device }} (Product: {{ comport.product ?? "?" }}, Serial:
- {{ comport.serial ?? "?" }})
- </option>
- </select>
- <FormSubLabel>
- <div class="text-blue-500 underline cursor-pointer" @click="loadComports">Reload Ports</div>
- </FormSubLabel>
+ <div v-if="['RNodeInterface', 'RNodeIPInterface'].includes(newInterfaceType)" class="mb-2">
+ <div v-if="newInterfaceType === 'RNodeInterface'" class="flex items-center mb-2">
+ <Toggle id="rnode-use-ip" v-model="newInterfaceRNodeUseIP" />
+ <FormLabel for="rnode-use-ip" class="ml-2">Connect over IP</FormLabel>
+ </div>
+
+ <div v-if="newInterfaceRNodeUseIP || newInterfaceType === 'RNodeIPInterface'" class="space-y-2">
+ <div>
+ <FormLabel class="mb-1">Host</FormLabel>
+ <input
+ v-model="newInterfaceRNodeIPHost"
+ type="text"
+ placeholder="e.g: 10.0.0.1"
+ class="input-field"
+ />
+ </div>
+ <div>
+ <FormLabel class="mb-1">Port</FormLabel>
+ <input
+ v-model="newInterfaceRNodeIPPort"
+ type="text"
+ placeholder="e.g: 7633"
+ class="input-field"
+ />
+ </div>
+ </div>
+ <div v-else>
+ <FormLabel class="mb-1">Port</FormLabel>
+ <select
+ v-model="newInterfacePort"
+ class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
+ >
+ <option v-for="comport of comports" :key="comport.device" :value="comport.device">
+ {{ comport.device }} (Product: {{ comport.product ?? "?" }}, Serial:
+ {{ comport.serial ?? "?" }})
+ </option>
+ </select>
+ <FormSubLabel>
+ <div class="text-blue-500 underline cursor-pointer" @click="loadComports">
+ Reload Ports
+ </div>
+ </FormSubLabel>
+ </div>
</div>
<!-- interface Frequency -->
@@ -694,7 +724,7 @@
</div>
<!-- RNodeInterface bitrate & link budget -->
- <ExpandingSection v-if="newInterfaceType === 'RNodeInterface'">
+ <ExpandingSection v-if="['RNodeInterface', 'RNodeIPInterface'].includes(newInterfaceType)">
<template #title>Calculated RNode Bitrate & Link Budget</template>
<template #content>
<div class="p-2 space-y-3">
@@ -908,7 +938,7 @@
</ExpandingSection>
<!-- optional RNodeInterface settings -->
- <ExpandingSection v-if="newInterfaceType === 'RNodeInterface'">
+ <ExpandingSection v-if="['RNodeInterface', 'RNodeIPInterface'].includes(newInterfaceType)">
<template #title>Optional RNodeInterface Settings</template>
<template #content>
<div class="p-2 space-y-3">
@@ -1127,6 +1157,9 @@ export default {
},
newInterfacePort: null,
+ newInterfaceRNodeUseIP: false,
+ newInterfaceRNodeIPHost: "localhost",
+ newInterfaceRNodeIPPort: "7633",
RNodeGHzValue: 0,
RNodeMHzValue: 0,
RNodekHzValue: 0,
@@ -1321,6 +1354,14 @@ export default {
// Port (For RNode, Serial, and KISS)
this.newInterfacePort = iface.port;
+ if (iface.type === "RNodeInterface" && iface.port && iface.port.startsWith("tcp://")) {
+ this.newInterfaceType = "RNodeIPInterface";
+ this.newInterfaceRNodeUseIP = true;
+ const address = iface.port.replace("tcp://", "");
+ const parts = address.split(":");
+ this.newInterfaceRNodeIPHost = parts[0];
+ this.newInterfaceRNodeIPPort = parts[1] || "7633";
+ }
// RNode Interface
this.newInterfaceFrequency = iface.frequency;
@@ -1425,7 +1466,10 @@ export default {
peers: this.I2PSettings.newInterfacePeers.join(","),
// rnode interface
- port: this.newInterfacePort,
+ port:
+ this.newInterfaceRNodeUseIP || this.newInterfaceType === "RNodeIPInterface"
+ ? `tcp://${this.newInterfaceRNodeIPHost}:${this.newInterfaceRNodeIPPort}`
+ : this.newInterfacePort,
frequency: this.calculateFrequencyInHz(),
bandwidth: this.newInterfaceBandwidth,
txpower: this.newInterfaceTxpower,
diff --git a/meshchatx/src/frontend/components/interfaces/Interface.vue b/meshchatx/src/frontend/components/interfaces/Interface.vue
index c0258243..a5572e2f 100644
--- a/meshchatx/src/frontend/components/interfaces/Interface.vue
+++ b/meshchatx/src/frontend/components/interfaces/Interface.vue
@@ -194,7 +194,7 @@ export default {
case "AutoInterface":
return "home-automation";
case "RNodeInterface":
- return "radio-tower";
+ return this.iface.port && this.iface.port.startsWith("tcp://") ? "lan-connect" : "radio-tower";
case "RNodeMultiInterface":
return "access-point-network";
case "TCPClientInterface":
@@ -226,6 +226,9 @@ export default {
if (this.iface.type === "SerialInterface") {
return `${this.iface.port} @ ${this.iface.speed || "9600"}bps`;
}
+ if (this.iface.type === "RNodeInterface" && this.iface.port && this.iface.port.startsWith("tcp://")) {
+ return `RNode over IP @ ${this.iface.port.replace("tcp://", "")}`;
+ }
if (this.iface.type === "AutoInterface") {
return "Auto-detect Ethernet and Wi-Fi peers";
}
diff --git a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
index a0519cc0..ee935464 100644
--- a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
+++ b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
@@ -62,7 +62,7 @@
<MaterialDesignIcon
:icon-name="reloadingRns ? 'refresh' : 'restart'"
class="w-4 h-4"
- :class="{ 'animate-spin': reloadingRns }"
+ :class="{ 'animate-spin-reverse': reloadingRns }"
/>
{{ reloadingRns ? $t("app.reloading_rns") : $t("app.reload_rns") }}
</button>
diff --git a/meshchatx/src/frontend/components/messages/ConversationViewer.vue b/meshchatx/src/frontend/components/messages/ConversationViewer.vue
index 8c2456f6..75b32668 100644
--- a/meshchatx/src/frontend/components/messages/ConversationViewer.vue
+++ b/meshchatx/src/frontend/components/messages/ConversationViewer.vue
@@ -920,9 +920,12 @@
>
<div
class="size-12 rounded-2xl bg-indigo-50 dark:bg-indigo-900/20 text-indigo-600 flex items-center justify-center group-hover:scale-110 transition-transform"
- :class="{ 'animate-spin': isSyncingPropagationNode }"
>
- <MaterialDesignIcon icon-name="sync" class="size-6" />
+ <MaterialDesignIcon
+ icon-name="sync"
+ class="size-6"
+ :class="{ 'animate-spin-reverse': isSyncingPropagationNode }"
+ />
</div>
<span class="text-sm font-bold text-gray-900 dark:text-zinc-100">{{
isSyncingPropagationNode ? "Syncing..." : "Sync Node"
diff --git a/meshchatx/src/frontend/components/network-visualiser/NetworkVisualiser.vue b/meshchatx/src/frontend/components/network-visualiser/NetworkVisualiser.vue
index d5fb42a1..c86e2652 100644
--- a/meshchatx/src/frontend/components/network-visualiser/NetworkVisualiser.vue
+++ b/meshchatx/src/frontend/components/network-visualiser/NetworkVisualiser.vue
@@ -1030,17 +1030,4 @@ export default {
background-image: radial-gradient(#18181b 1px, transparent 1px);
background-size: 32px 32px;
}
-
-@keyframes spin-reverse {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(-360deg);
- }
-}
-
-.animate-spin-reverse {
- animation: spin-reverse 1s linear infinite;
-}
</style>
diff --git a/meshchatx/src/frontend/components/rnstatus/RNStatusPage.vue b/meshchatx/src/frontend/components/rnstatus/RNStatusPage.vue
index 8ff7d996..cd6a800e 100644
--- a/meshchatx/src/frontend/components/rnstatus/RNStatusPage.vue
+++ b/meshchatx/src/frontend/components/rnstatus/RNStatusPage.vue
@@ -27,7 +27,7 @@
<MaterialDesignIcon
icon-name="refresh"
class="w-4 h-4"
- :class="{ 'animate-spin': isLoading }"
+ :class="{ 'animate-spin-reverse': isLoading }"
/>
Refresh
</button>
diff --git a/meshchatx/src/frontend/components/settings/SettingsPage.vue b/meshchatx/src/frontend/components/settings/SettingsPage.vue
index 49c82912..c4557e32 100644
--- a/meshchatx/src/frontend/components/settings/SettingsPage.vue
+++ b/meshchatx/src/frontend/components/settings/SettingsPage.vue
@@ -846,7 +846,7 @@
<MaterialDesignIcon
:icon-name="reloadingRns ? 'refresh' : 'restart'"
class="w-5 h-5"
- :class="{ 'animate-spin': reloadingRns }"
+ :class="{ 'animate-spin-reverse': reloadingRns }"
/>
<span>{{ reloadingRns ? $t("app.reloading_rns") : $t("app.reload_rns") }}</span>
</button>
diff --git a/meshchatx/src/frontend/components/tools/RNPathPage.vue b/meshchatx/src/frontend/components/tools/RNPathPage.vue
index 423b6ba0..effd5b8c 100644
--- a/meshchatx/src/frontend/components/tools/RNPathPage.vue
+++ b/meshchatx/src/frontend/components/tools/RNPathPage.vue
@@ -20,7 +20,11 @@
title="Refresh"
@click="refreshAll"
>
- <MaterialDesignIcon icon-name="refresh" class="size-6" :class="{ 'animate-spin': isLoading }" />
+ <MaterialDesignIcon
+ icon-name="refresh"
+ class="size-6"
+ :class="{ 'animate-spin-reverse': isLoading }"
+ />
</button>
</div>
</div>
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────